package com.nofeng.wx.service.stock;

import com.nofeng.wx.controller.stock.view.ChartPointView;
import com.nofeng.wx.controller.stock.view.StockReportMinuteRsiView;
import com.nofeng.wx.utils.BaseUtils;
import org.apache.commons.collections4.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class BacktestEvaluator {

    // 定义回测分数结果对象
    public static class BacktestScoreResult {
        private double totalProfitRatio; // 总收益比例
        private double maxDrawdownRatio; // 最大回撤比例
        private double sharpeRatio; // 夏普比率
        private double winRate; // 胜率

        public BacktestScoreResult(double totalProfitRatio, double maxDrawdownRatio, double sharpeRatio, double winRate) {
            this.totalProfitRatio = totalProfitRatio;
            this.maxDrawdownRatio = maxDrawdownRatio;
            this.sharpeRatio = sharpeRatio;
            this.winRate = winRate;
        }

        public double getTotalProfitRatio() {
            return totalProfitRatio;
        }

        public double getMaxDrawdownRatio() {
            return maxDrawdownRatio;
        }

        public double getSharpeRatio() {
            return sharpeRatio;
        }

        public double getWinRate() {
            return winRate;
        }
    }


    /**
     * 根据给定的StockReportMinuteRsiView对象进行回测分数评估，支持允许多次买入，且每次买入股数可设置为5000
     *
     * @param stockReport 包含交易相关数据的对象
     * @return 包含总收益比例、最大回撤比例、夏普比率和胜率的BacktestScoreResult对象，可根据实际调整逻辑
     */
    public static BacktestScoreResult evaluateBacktestScore(StockReportMinuteRsiView stockReport, BigDecimal fixedShareQuantity) {
        if (stockReport == null || stockReport.getBuyPointViewList() == null || stockReport.getCloseList() == null || stockReport.getDayList() == null) {
            throw new IllegalArgumentException("输入的StockReportMinuteRsiView对象或其关键属性不能为空");
        }

        BigDecimal initialBalance = new BigDecimal("400000000"); // 这里设定初始资金为400000，可按实际调整
        BigDecimal currentBalance = initialBalance;

        // 使用一个列表来记录每次买入的价格以及对应的持股数量（这里设置每次买入股数为5000）
        // 元素类型可以自定义一个内部类，这里简单用数组表示，{买入价格, 持股数量}
        List<List<BigDecimal>> holdingPositions = java.util.Collections.synchronizedList(new java.util.ArrayList<>());

        List<BigDecimal> closeList = stockReport.getCloseList();
        List<String> dayList = stockReport.getDayList();
        List<ChartPointView> buyPointViewList = stockReport.getBuyPointViewList();


        // 用于记录资产净值的历史峰值
        BigDecimal peakBalance = initialBalance;
        // 用于记录最大回撤比例
        BigDecimal maxDrawdown = BigDecimal.ZERO;

        // 记录每次交易后的资产净值，用于计算收益率序列来求标准差（初始值为初始资金）
        List<BigDecimal> netAssetValues = new ArrayList<>();
        netAssetValues.add(initialBalance);

        // 记录盈利交易次数
        int profitableTradeCount = 0;
        // 记录总交易次数（买入或卖出都算一次交易）
        int totalTradeCount = 0;

        double pt=0.00;

        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        ChartPointView lastDay = new ChartPointView();
        lastDay.setValue("L");
        lastDay.setXAxis(dayList.get(dayList.size()-1));
        buyPointViewList.add(lastDay);

        // 遍历交易信号点
        for (ChartPointView point : buyPointViewList) {
            String date = point.getXAxis();
            String action = point.getValue();

            int index = dayList.indexOf(date);
            if (index == -1) {
                throw new IllegalArgumentException("在日期列表中找不到对应的交易日期: " + date);
            }

            totalTradeCount++;

            if (action.contains("B")) {
                // 买入操作，记录买入价格和持股数量（按固定股数5000）
                BigDecimal buyPrice = closeList.get(index);
                BigDecimal totalBuyCost = buyPrice.multiply(fixedShareQuantity);
                if (currentBalance.compareTo(totalBuyCost) < 0) {
                    // 资金不足，无法买入，可根据实际情况添加相应处理逻辑，这里简单跳过本次买入
                    continue;
                }
                List<BigDecimal> position = java.util.Arrays.asList(buyPrice, fixedShareQuantity);
                holdingPositions.add(position);
                currentBalance = currentBalance.subtract(totalBuyCost);
            } else {
                // 卖出操作，遍历所有持股，按先进先出原则卖出
                BigDecimal sellPrice = closeList.get(index);
                synchronized (holdingPositions) {
                    for (int i = 0; i < holdingPositions.size(); i++) {
                        List<BigDecimal> position = holdingPositions.get(i);
                        BigDecimal positionBuyPrice = position.get(0);
                        BigDecimal positionQuantity = position.get(1);
                        if (positionQuantity.compareTo(BigDecimal.ZERO) > 0) {
                            double ptDouble = (sellPrice.doubleValue() - positionBuyPrice.doubleValue()) / positionBuyPrice.doubleValue();
                            pt=pt+ptDouble;
                            System.out.println(date+"卖出收益:"+BaseUtils.doubleToPtStr(ptDouble)+",买入:"+ positionBuyPrice.doubleValue()+",卖出:"+sellPrice.doubleValue());
                            // 计算本次卖出收益并更新余额和持股数量
                            BigDecimal profit = sellPrice.multiply(positionQuantity);
                            currentBalance = currentBalance.add(profit);
                            position.set(1, BigDecimal.ZERO); // 卖出后持股数量设为0
                            if ( ptDouble> 0) {
                                profitableTradeCount++;
                            }

//                            if(!action.contains("L")) {
//                                break;
//                            }
                        }
                    }
                }
                // 清理持股数量为0的记录
//                holdingPositions.removeIf(position -> position.get(1).compareTo(BigDecimal.ZERO) == 0);
            }

            // 计算当前资产净值（包含持有的股票价值）
            BigDecimal currentAssetValue = currentBalance;
            for (List<BigDecimal> position : holdingPositions) {
                BigDecimal positionBuyPrice = position.get(0);
                BigDecimal positionQuantity = position.get(1);
                BigDecimal currentPositionValue = positionBuyPrice.multiply(positionQuantity);
                currentAssetValue = currentAssetValue.add(currentPositionValue);
            }

            // 更新资产净值的历史峰值，并计算当前回撤比例
            if (currentAssetValue.compareTo(peakBalance) > 0) {
                peakBalance = currentAssetValue;
            } else {
                BigDecimal drawdown = (peakBalance.subtract(currentAssetValue)).divide(peakBalance, 2, BigDecimal.ROUND_HALF_UP);
                if (drawdown.compareTo(maxDrawdown) > 0) {
                    maxDrawdown = drawdown;
                }
            }

            // 记录本次交易后的资产净值
            netAssetValues.add(currentAssetValue);


            // 更新总投资小时数（通过日期格式解析并计算时间差来更新总小时数）
            if (index > 0) {
                try {
                    Date currentDate = dateFormat.parse(dayList.get(index));
                    Date prevDate = dateFormat.parse(dayList.get(index - 1));
                    long diffInMillis = currentDate.getTime() - prevDate.getTime();
                    int diffInHours = (int) (diffInMillis / (1000 * 60 * 60));
                } catch (ParseException e) {
                    e.printStackTrace();
                }
            }
        }

        // 处理剩余未卖出的股票，假设以最后一天收盘价卖出（可根据实际情况调整）
        synchronized (holdingPositions) {
            if (!holdingPositions.isEmpty()) {
                int lastIndex = closeList.size() - 1;
                BigDecimal lastClosePrice = closeList.get(lastIndex);
                for (List<BigDecimal> position : holdingPositions) {
                    BigDecimal positionBuyPrice = position.get(0);
                    BigDecimal positionQuantity = position.get(1);
                    BigDecimal profit = (lastClosePrice).multiply(positionQuantity);
                    currentBalance = currentBalance.add(profit);
                }
            }
        }

        // 计算总收益比例
        double totalProfitRatio = 0;
        if (initialBalance.compareTo(BigDecimal.ZERO)!= 0) {
            totalProfitRatio = currentBalance.subtract(initialBalance).doubleValue() / initialBalance.doubleValue();
        }

        double ptAll = (closeList.get(closeList.size() - 1).doubleValue() - closeList.get(0).doubleValue()) / closeList.get(0).doubleValue();
        System.out.println("原始收益："+BaseUtils.doubleToPtStr(ptAll));
        if(CollectionUtils.isNotEmpty(holdingPositions)) {
            double ptAVG = new BigDecimal(pt).divide(new BigDecimal(holdingPositions.size()), RoundingMode.DOWN).doubleValue();
            System.out.println("操作收益：" + BaseUtils.doubleToPtStr(pt) + ",收益差：" + BaseUtils.doubleToPtStr(pt - ptAll));
            System.out.println("操作总次数：" + buyPointViewList.size() + ",总天数：" + stockReport.getDayList().size() + ",日均：" + stockReport.getDayList().size() / buyPointViewList.size());
        }

        // 计算夏普比率相关逻辑
        double riskFreeRate = 0.03; // 假设无风险利率为年化3%，可根据实际调整
        List<Double> returnsList = new ArrayList<>();
        for (int i = 1; i < netAssetValues.size(); i++) {
            BigDecimal prevValue = netAssetValues.get(i - 1);
            BigDecimal currentValue = netAssetValues.get(i);
            double returnRate = currentValue.subtract(prevValue).doubleValue() / prevValue.doubleValue();
            returnsList.add(returnRate);
        }
        double stdDeviation = calculateStandardDeviation(returnsList);
        double sharpeRatio = (totalProfitRatio - riskFreeRate) / stdDeviation;

        // 计算胜率
        double winRate = 0;
        if (totalTradeCount > 0) {
            winRate = (double) profitableTradeCount / totalTradeCount;
        }

        return new BacktestScoreResult(totalProfitRatio, maxDrawdown.doubleValue(), sharpeRatio, winRate);
    }

    // 辅助方法，用于计算收益率序列的标准差
    private static double calculateStandardDeviation(List<Double> dataList) {
        if (dataList.size() < 2) {
            return 0;
        }
        double sum = 0;
        double sumSquaredDiff = 0;
        for (double num : dataList) {
            sum += num;
        }
        double mean = sum / dataList.size();
        for (double num : dataList) {
            double diff = num - mean;
            sumSquaredDiff += diff * diff;
        }
        return Math.sqrt(sumSquaredDiff / (dataList.size() - 1));
    }
}